import os
import time
import datetime
import numpy as np
import matplotlib.pyplot as plt
import numpy.polynomial.chebyshev as chebyshev
import numpy.linalg as linalg
from measurement.preprocess_probe_data import *
from pylab import mpl
from utils.mylib_db import *
from utils.mylib_utils import *
from measurement.measurement_utils import *
import multiprocessing

"""
1. 生成报告中使用的图片，图片以报告中内容要求命名，如下：
"""
# 废弃不用
figure_name_list_ld = [
    "激光-密度-比对图",
    "激光-温度-比对图",
    "激光-风场-比对图",
    "激光-铁密度-比对图",
    "激光-钠密度-比对图",
    "激光-[device_id]-密度-不确定度分量-饼图",
    "激光-[device_id]-密度-不确定度分量-棒状图",
    "激光-[device_id]-密度-不确定度分量-廓线折线图",
    "激光-[device_id]-温度-不确定度分量-饼图",
    "激光-[device_id]-温度-不确定度分量-棒状图",
    "激光-[device_id]-温度-不确定度分量-廓线折线图",
    "激光-[device_id]-风场-不确定度分量-饼图",
    "激光-[device_id]-风场-不确定度分量-棒状图",
    "激光-[device_id]-风场-不确定度分量-廓线折线图",
    "激光-[device_id]-铁密度-不确定度分量-饼图",
    "激光-[device_id]-铁密度-不确定度分量-棒状图",
    "激光-[device_id]-铁密度-不确定度分量-廓线折线图",
    "激光-[device_id]-钠密度-不确定度分量-饼图",
    "激光-[device_id]-钠密度-不确定度分量-棒状图",
    "激光-[device_id]-钠密度-不确定度分量-廓线折线图",
]
# 废弃不用
figure_name_list_qq = [
    "原位气球-温度-比对图",
    "原位气球-压力-比对图",
    "原位气球-风场-比对图",
    "原位气球-[device_id]-温度-不确定度分量-饼图",
    "原位气球-[device_id]-温度-不确定度分量-棒状图",
    "原位气球-[device_id]-温度-不确定度分量-廓线折线图",
    "原位气球-[device_id]-压力-不确定度分量-饼图",
    "原位气球-[device_id]-压力-不确定度分量-棒状图",
    "原位气球-[device_id]-压力-不确定度分量-廓线折线图",
    "原位气球-[device_id]-风场-不确定度分量-饼图",
    "原位气球-[device_id]-风场-不确定度分量-棒状图",
    "原位气球-[device_id]-风场-不确定度分量-廓线折线图"
]
# 废弃不用
figure_name_list_rocket = [
    "原位火箭-温度-比对图",
    "原位火箭-压力-比对图",
    "原位火箭-风场-比对图",
    "原位火箭-密度-比对图",
    "原位火箭-[device_id]-温度-不确定度分量-饼图",
    "原位火箭-[device_id]-温度-不确定度分量-棒状图",
    "原位火箭-[device_id]-温度-不确定度分量-廓线折线图",
    "原位火箭-[device_id]-压力-不确定度分量-饼图",
    "原位火箭-[device_id]-压力-不确定度分量-棒状图",
    "原位火箭-[device_id]-压力-不确定度分量-廓线折线图",
    "原位火箭-[device_id]-风场-不确定度分量-饼图",
    "原位火箭-[device_id]-风场-不确定度分量-棒状图",
    "原位火箭-[device_id]-风场-不确定度分量-廓线折线图",
    "原位火箭-[device_id]-密度-不确定度分量-饼图",
    "原位火箭-[device_id]-密度-不确定度分量-棒状图",
    "原位火箭-[device_id]-密度-不确定度分量-廓线折线图"
]

# 大气密度、大气压力、钠密度、铁密度没有棒状图
figure_name_list_ld_map = {
    "D": "激光-密度-比对图",
    "T": "激光-温度-比对图",
    "W": "激光-风场-比对图",
    "F": "激光-铁密度-比对图",
    "N": "激光-钠密度-比对图",
    "T-Pie": "激光-[device_id]-温度-不确定度分量-饼图",        # Pie Figure
    "T-Err": "激光-[device_id]-温度-不确定度分量-棒状图",      # Error Bar Figure
    "T-Con": "激光-[device_id]-温度-不确定度分量-廓线折线图",  # Contour Figure
    "D-Pie": "激光-[device_id]-密度-不确定度分量-饼图",
    # "D-Err": "激光-[device_id]-密度-不确定度分量-棒状图",
    "D-Con": "激光-[device_id]-密度-不确定度分量-廓线折线图",
    "W-Pie": "激光-[device_id]-风场-不确定度分量-饼图",
    "W-Err": "激光-[device_id]-风场-不确定度分量-棒状图",
    "W-Con": "激光-[device_id]-风场-不确定度分量-廓线折线图",
    "F-Pie": "激光-[device_id]-铁密度-不确定度分量-饼图",
    # "F-Err": "激光-[device_id]-铁密度-不确定度分量-棒状图",
    "F-Con": "激光-[device_id]-铁密度-不确定度分量-廓线折线图",
    "N-Pie": "激光-[device_id]-钠密度-不确定度分量-饼图",
    # "N-Err": "激光-[device_id]-钠密度-不确定度分量-棒状图",
    "N-Con": "激光-[device_id]-钠密度-不确定度分量-廓线折线图",
}
# 大气密度、大气压力、钠密度、铁密度没有棒状图
figure_name_list_yw_balloon_map = {
    "T": "原位气球-温度-比对图",
    "P": "原位气球-压力-比对图",
    "W": "原位气球-风场-比对图",
    "T-Pie": "原位气球-[device_id]-温度-不确定度分量-饼图",
    "T-Err": "原位气球-[device_id]-温度-不确定度分量-棒状图",
    "T-Con": "原位气球-[device_id]-温度-不确定度分量-廓线折线图",
    "P-Pie": "原位气球-[device_id]-压力-不确定度分量-饼图",
    # "P-Err": "原位气球-[device_id]-压力-不确定度分量-棒状图",
    "P-Con": "原位气球-[device_id]-压力-不确定度分量-廓线折线图",
    "W-Pie": "原位气球-[device_id]-风场-不确定度分量-饼图",
    "W-Err": "原位气球-[device_id]-风场-不确定度分量-棒状图",
    "W-Con": "原位气球-[device_id]-风场-不确定度分量-廓线折线图"
}
# 大气密度、大气压力、钠密度、铁密度没有棒状图
figure_name_list_yw_rocket_map = {
    "T": "原位火箭-温度-比对图",
    "P": "原位火箭-压力-比对图",
    "W": "原位火箭-风场-比对图",
    "D": "原位火箭-密度-比对图",
    "T-Pie": "原位火箭-[device_id]-温度-不确定度分量-饼图",
    "T-Err": "原位火箭-[device_id]-温度-不确定度分量-棒状图",
    "T-Con": "原位火箭-[device_id]-温度-不确定度分量-廓线折线图",
    "P-Pie": "原位火箭-[device_id]-压力-不确定度分量-饼图",
    # "P-Err": "原位火箭-[device_id]-压力-不确定度分量-棒状图",
    "P-Con": "原位火箭-[device_id]-压力-不确定度分量-廓线折线图",
    "W-Pie": "原位火箭-[device_id]-风场-不确定度分量-饼图",
    "W-Err": "原位火箭-[device_id]-风场-不确定度分量-棒状图",
    "W-Con": "原位火箭-[device_id]-风场-不确定度分量-廓线折线图",
    "D-Pie": "原位火箭-[device_id]-密度-不确定度分量-饼图",
    # "D-Err": "原位火箭-[device_id]-密度-不确定度分量-棒状图",
    "D-Con": "原位火箭-[device_id]-密度-不确定度分量-廓线折线图"
}

# 不确定度分量名称
# 激光雷达
uncertainty_component_name_ld = {
    "U1": "探测点光子噪声",
    "U2": "参考点光子噪声",
    "U3": "背景噪声",
    "U4": "探测点渡越时间",
    "U5": "参考点渡越时间",
    "U6": "参考温度",
    "U7": "f0通道光子噪声",
    "U8": "f+通道光子噪声",
    "U9": "f-通道光子噪声",
    "U10": "f0通道背景噪声",
    "U11": "f+通道背景噪声",
    "U12": "f-通道背景噪声",
    "U13": "f0通道渡越时间",
    "U14": "f+通道渡越时间",
    "U15": "f-通道渡越时间",
    "U16": "温风查算表温度分辨率",
    "U17": "拉曼体制高阶通道光子噪声",
    "U18": "拉曼体制低阶通道光子噪声",
    "U19": "拉曼体制高阶通道背景噪声",
    "U20": "拉曼体制低阶通道背景噪声",
    "U21": "拉曼体制高阶通道渡越时间",
    "U22": "拉曼体制低阶通道渡越时间"
}

# 原位探测
uncertainty_component_name_yw = {
    "U1": "由温度传感器自身准确度引入",
    "U2": "由太阳辐射引入",
    "U3": "由压力传感器自身准确度引入",
    "U4": "由温度特性引入",
    "U5": "由气动效应引入",
    "U6": "由传感器响应时间引入",
    "U7": "由迟滞效应引入",
    "U8": "由探空仪传感结构引入",
    "U9": "由数据传输引入",
    "U10": "由数据处理引入",
    "U11": "由气球速度测量引入",
    "U12": "由大气风速解算引入",
    "U13": "由气球风阻系数引入",
    "U14": "由风压引入",
    "U15": "由火箭速度测量引入",
    "U16": "由大气风速解算引入",
    "U17": "由火箭风阻系数引入",
}


# db_table_function_fitting = "bt_data_analysis_function_fitting"
db_table_report_figure = "bt_report_figure"
db_data_head_table = "bt_data_head"

MAX_LEN = 1000     # 每次插入数据库的最大数据条数（批量插入功能）

# 函数图像保存路径（固定不变）
figure_save_path = "media/data/report_figure"

def dataReportFigureGen(param):
    """
    数据报告图片生成接口，包括：
        1. 筛选数据
        2. 画图
        3. 按名称，保存到指定位置
    """
    print("进入报告出图过程..")
    print("参数情况：")
    print(param)
    # figure_save_path = "%s/%s" % (os.getcwd(), related_figure_save_path)
    # figure_save_path = figure_save_path.replace("\\", "/")

    # 参数处理
    task_id = param.get('task_id') if param.get('task_id') is not None else get_current_timestamp()                     # TaskId默认为当前时间
    process_num = int(param.get('process_num')) if param.get('process_num') is not None else DEFAULT_PROCESS_NUM        # 默认4个进程同时读入文件
    user_id = int(param.get('user_id')) if param.get('user_id') is not None else -1                                     # 当前登录用户

    # 筛选条件（数据查询参数）
    data_source = int(param.get('data_source')) if param.get('data_source') is not None else 0                          # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据

    # 指定报告需要的数据：batch_id 和 head_id_list 只需要提供1个就行，两个都提供以batch_id为准）
    batch_id = param.get('batch_id') if param.get('batch_id') is not None else ""                                       # 报告所需数据的batch_id值，只支持1个batch_id，默认值为：""
    head_id_list = param.get('head_id_list') if param.get('head_id_list') is not None else ""                           # 默认值为：""，格式为 "1,2,3,4,5"

    # 报告的编号，用来作为保存同一个报告图片的目录名称
    report_id = param.get('report_id') if param.get('report_id') is not None else get_current_timestamp()               # 默认为当前时间

    # 报告图片存储位置指定（指定的报告路径不在/media下时，不能外链访问）
    fig_save_path = param.get('fig_save_path') if param.get('fig_save_path') is not None else figure_save_path          # 函数拟合图像保存位置


    # 判断参数合理性
    if batch_id == "" and head_id_list == "":
        print("参数错误：batch_id 和 head_id_list 至少需要提供一个！")
        return {"code": -1, "msg": "参数错误：batch_id 和 head_id_list 至少需要提供一个！"}

    conf = {
        'TaskId': task_id,
        # 任务种类：0:数据录入；1:数据预处理；2:不确定度评定;3:数据分析数据处理;
        # 5:一键评定;6:评定结果查询（文件上传）；7:不确定度校验（文件上传）；8:数据比对不同设备；9:数据比对不同时间
        # 10:仿真模拟程序调用任务;11:报告生成图片数据任务
        "TaskType": 11,
        'FigSavePath': fig_save_path if fig_save_path != "" else figure_save_path,
        'MaxDataNumber': 30,   # 控制生成图片的廓线数量（默认30）

        # 数据源
        'DataSource': data_source,
        # batch_id 和 head_id_list 至少有一个，同时存在时，BatchId优先级高
        'HeadIdList': head_id_list,
        'BatchId': batch_id,
        'ReportId': report_id,

        'DataLevel': 1,
        # 用户文件导入相关
        "ProcessNum": process_num,
        "UserId": user_id,

        'SaveFlag': 0,              # 报告图片任务信息不存数据库（图片存库）

        'DbHeadTable': "",          # 数据筛选源Head表
        'DbTable': "",              # 数据筛选源Data表
        'DbSaveHeadTable': "",      # 数据存储Head表
        'DbSaveTable': "",          # 数据存储Data表
    }
    print("参数值：")
    print(conf)

    # 不支持对最近一次录入的数据进行预处理！（但是代码里面已经支持该情况，需测试后启用）
    if conf['DataSource'] == 3:
        # print("不支持对最近一次录入的数据进行预处理！（后续可增加此功能）")
        # return {"code": -1, "msg": "不支持对最近一次录入的数据进行预处理！"}
        print("对最近一次录入的数据进行处理..")

    if conf['DataSource'] == 2 and conf['ImportFilePath'] == "":
        return {"code": -1, "msg": "数据源选择了“文件导入”，需指定导入文件或文件夹路径！"}

    # 参数检查通过
    # 保存参数 TODO
    # param_conf_id = saveAnalysisParametersDataIntoDB(conf)
    # conf["ParameterSetting"] = param_conf_id
    # print(param_conf_id)
    # return 0

    """
    # 指定数据筛选库表和存储库表
    # 指定保存数据的库表（预处理数据对象是L2,如果存库，进正式表，如果不存，临时表）
    # 针对其他场景，进行修改（导入文件，存临时表）
    # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    # 保存数据
    if conf['SaveFlag'] == 1:
        conf['DbSaveHeadTable'] = db_data_head_table
        conf['DbSaveTable'] = db_raw_l2_precessed_table
    else:
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2

    # 获取数据（conf['DataSource'] == 3情况，需根据任务表是否存库字段确定）
    if conf['DataSource'] == 0:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_precessed_table
    elif conf['DataSource'] == 1:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_data_table
    elif conf['DataSource'] == 2:
        #  2:文件导入：临时表，结果不存库
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2
    # elif conf['DataSource'] == 3:  # 查任务表后进行确认数据存储源数据库表
    else:
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2
    """

    # 设置任务开始
    TASK_ID = conf['TaskId']
    if TASK_ID != "":
        ret = setTaskStatus(TASK_ID, conf['TaskType'], 0, -1, conf['DataSource'])
        if ret != "OK":
            print("setTaskStatus: %s" % ret)
            return ret

    # 对data_head_id_list中的所有数据进行高度分辨率处理和时间分辨率处理
    processes = []
    process = multiprocessing.Process(target=runReportFigureGenTaskFunc, args=(conf, TASK_ID))     # 正式运行用（捕捉错误，设置任务完成状态）
    # process = multiprocessing.Process(target=runReportFigureGenTask, args=(conf, TASK_ID))       # 开发调试用，暴露出错误代码行号
    processes.append(process)
    process.start()

    return 0


def runReportFigureGenTaskFunc(conf, TASK_ID):
    try:
        start_time = time.time()    # 计时开始
        ret = runReportFigureGenTask(conf, TASK_ID)
        print(ret)
        end_time = time.time()      # 计时结束
        #计算代码运行时间(ms)
        run_time = (end_time - start_time) * 1000
        #print('代码运行时间为{:.2f} s'.format(run_time))
        print('代码运行时间为%d ms' % run_time)

        if ret != 0:
            print("设置任务状态为 完成状态 ！")
            setTaskStatusFinish(conf)

        # 以tkinter窗口形式给出系统错误提示
        if 'code' in ret.keys() and ret['code'] == -1:
            err_msg = ret['msg']
            print("错误：", err_msg)
            show_msg_to_tkinter(err_msg)

    except Exception as e:
        print("runDataAnalysisTaskFunc_Error: %s" % e)
        setTaskStatusFinish(conf)
        # 以tkinter窗口形式给出系统错误提示
        show_msg_to_tkinter(e)


def runReportFigureGenTask(conf, TASK_ID):
    ######################################
    # 保存数据
    """
    if conf['DataSource'] == 2:
        print("开始导入用户数据..")
        ret = callReadFileProcessAndWaitFinish(conf, conf['ImportFilePath'], conf['ProcessNum'], TASK_ID)
        print("读取用户文件结束！")
        print("进入筛选文件数据阶段..")
    """
    ######################################
    # 获取数据
    print("开始筛选文件数据阶段..")
    head_id_list = conf['HeadIdList']

    # 按查询条件获取探测数据（head_id_list）
    data_head_id_list = []

    # 如果没有指定head_id_list，获取最近一次预处理后的数据（表bt_data或表bt_temporary_data）
    """
    data_batch_id = -1
    data_batch_location = 1   # 默认预处理的数据存库，数据在正式预处理后的数据库表
    """
    data_batch_id = conf['BatchId']

    # 查询流程：1）优先检查是否提供了headid，2）其次根据datasource判断是按条件检索或检索最近一次任务的结果
    if head_id_list != "":
        # 如果参数中指定了head_id_list
        print("参数指定了Head_id_list!优先处理..")
        # data_head_id_list = []
        head_id_list_arr = head_id_list.split(",")
        if len(head_id_list_arr) > 0:
            for head_id in head_id_list_arr:
                print(head_id)
                ret = getHeadIdInfoByHeadId(int(head_id), 0, 1)
                if ret != "":
                    data_head_id_list.append(ret)
        else:
            print("参数head_id_list格式错误！")
            return {"code": -1, "msg": "提供的参数head_id_list格式错误！"}

    elif data_batch_id != "":
        print("通过BatchId获取评定后的数据")
        # data_head_id_list = []

        # 逻辑等同于获取最近一次评定的数据
        # 根据任务确定数据库表筛选数据情况
        print("取最新一次任务处理结束的数据..")
        data_batch_info = getLatestDataBatchId(2)
        if data_batch_info == "":
            print("错误：未找到最近录入的数据")
            return {"code": -1, "msg": "未找到最近录入的数据！"}
        data_batch_id = data_batch_info['KeyName']
        print("data_batch_id: %s" % data_batch_id)

        # 确定数据录入的数据存储数据库源表（TODO：校验？）
        if data_batch_info['IsSaveIntoDb'] == 1:
            conf['DbHeadTable'] = db_data_head_table
            conf['DbTable'] = db_raw_l2_data_table
        elif data_batch_info['IsSaveIntoDb'] == 0:
            conf['DbHeadTable'] = db_temporary_data_head_table
            conf['DbTable'] = db_temporary_data_table_l2

        data_head_id_list = getHeadIdByBatchIdV3(conf, data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}

    else:
        print("参数错误！")
        return {"code": -1, "msg": "参数错误，请检查传入的参数！"}

    """
    elif conf['DataSource'] == 0 or conf['DataSource'] == 1:
        # 对数据源 0:数据库预处理数据，1:数据库原始数据 2:文件导入;
        print("根据数据源，按条件筛选head_id_list..")
        if conf['ValueType'] == "":
            print("参量类型ValueType不能为空")
            return {"code": -1, "msg": "当前选择的数据源，参量类型参数不能为空！"}

        # data_head_id_list = queryDataByCondition(conf, 0)
        data_head_id_list = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询
        if len(data_head_id_list) == 0:
            print("错误：未检索到探测数据，请重新设置检索条件！")
            return {"code": -1, "msg": "错误：未检索到探测数据，请重新设置检索条件！"}

    elif conf['DataSource'] == 3:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        # 根据任务确定数据库表筛选数据情况
        print("取最新一次任务处理结束的数据..")
        data_batch_info = getLatestDataBatchId(0)
        if data_batch_info == "":
            print("错误：未找到最近录入的数据")
            return {"code": -1, "msg": "未找到最近录入的数据！"}
        data_batch_id = data_batch_info['KeyName']
        print("data_batch_id: %s" % data_batch_id)

        # 确定数据录入的数据存储数据库源表（TODO：校验？）
        if data_batch_info['IsSaveIntoDb'] == 1:
            conf['DbHeadTable'] = db_data_head_table
            conf['DbTable'] = db_raw_l2_data_table
        elif data_batch_info['IsSaveIntoDb'] == 0:
            conf['DbHeadTable'] = db_temporary_data_head_table
            conf['DbTable'] = db_temporary_data_table_l2

        data_head_id_list = getHeadIdByBatchId(data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}

    elif conf['DataSource'] == 2:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        print("获取刚刚导入的用户文件数据..")
        data_batch_id = TASK_ID
        print("data_batch_id: %s" % data_batch_id)

        data_head_id_list = getHeadIdByBatchIdV3(conf, data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}
    """


    print("data_head_id_list: ")
    print(data_head_id_list)
    print("检索到HearID数量：%d" % len(data_head_id_list))
    if len(data_head_id_list) == 0:
        print("未获取到探测数据！")
        return {"code": -1, "msg": "未获取到探测数据"}

    time.sleep(2)
    # return 0

    # # 确定数据库表
    # # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    # if conf['DataSource'] == 0:
    #     conf['DbHeadTable'] = db_data_head_table
    #     conf['DbTable'] = db_raw_l2_precessed_table
    # elif conf['DataSource'] == 1:
    #     conf['DbHeadTable'] = db_data_head_table
    #     conf['DbTable'] = db_raw_l2_data_table
    # else:
    #     conf['DbHeadTable'] = db_temporary_data_head_table
    #     conf['DbTable'] = db_temporary_data_table_l2

    # 清除临时表数据，参数和setTaskStatus中任务类型一致
    clear_db_temporary_table_and_keep_last_same_type_task(3)

    # # 设置任务开始
    # TASK_ID = task_id
    # if TASK_ID != "":
    #     ret = setTaskStatus(TASK_ID, 3, 0, -1, conf['DataSource'])
    #     if ret != "OK":
    #         print("setTaskStatus: %s" % ret)
    #         return ret
    #
    # # 对data_head_id_list中的所有数据进行高度分辨率处理和时间分辨率处理
    # processes = []
    # process = multiprocessing.Process(target=taskDataAnalysis, args=(data_head_id_list, conf, data_batch_location, data_level, TASK_ID))
    # processes.append(process)
    # process.start()

    # 获取所有设备信息，用于画图的名称（title）
    all_devices_obj = getAllDeviceInfo2309(1, "Id")

    #taskDataAnalysis(data_head_id_list, conf, data_batch_location, conf['DataLevel'], TASK_ID)
    # 生成多条廓线综合对比图
    ret1 = taskReportFigureGen(data_head_id_list, conf, conf['DataLevel'], TASK_ID)
    # 生成不确定度结果图：饼图、折线图、棒状图
    ret2 = taskReportFigureGenWithGumResult(data_head_id_list, conf, conf['DataLevel'], all_devices_obj, TASK_ID)

    print("全部廓线数量：", len(data_head_id_list))
    # print("统合图包含廓线数量：", ret1)
    print("单项图画图廓线数量：", ret2)

    # 测试
    return {"code": 0, "msg": ""}


##########################################
# 1. 针对同类（激光雷达/原位气球/原位火箭）的所有廓线生成综合廓线图像
# 仅需要原始探测数据
def taskReportFigureGen(data_head_id_list, conf, data_level, TASK_ID):
    print("综合报告图像生成：")
    # print(data_head_id_list)
    # print(conf)

    data_list_json = {}    # 缓存原始和预处理中间数据，格式：{"head_id1":[], "head_id2":[], "head_id3":[], }
    data_list_device_id_json = {}   # 保存设备Id
    # 保存同类value_type的数据，画曲线
    data_linda_value_type_obj = {}    # {'T':{"081010":[X，Y], "081112": [X，Y]}, 'D':{}}
    data_yw_balloon_value_type_obj = {}
    data_yw_rocket_value_type_obj = {}

    # [总]获取所有预处理数据，保存到data_list_json
    # 根据head_id，测量数据类型value_type，获取原始数据
    for (head_id, value_type, start_time, end_time, station_id, device_id, file_name, file_store_path) in data_head_id_list:
        print("==================================")
        print("head_id = %d, value_type = %s" % (head_id, value_type))
        # raw_data = getRawDataByHeadIdInJsonObjectList(conf, head_id, data_level)        # data_type指定从L0或L2原始数据表中获取数据

        # 获取L2文件路径，读取文件中探测数据
        raw_data = parsingRadarProbeDataFromRawFile2309(file_store_path, file_name, head_id, TASK_ID, 1)
        # 按高度排序
        raw_data = sorted(raw_data, key=lambda x: x['Height'])

        if raw_data == "":
            print("错误：未找到最近录入的原始数据")
            continue

        print("raw_data:原始数据条数: %d" % len(raw_data))
        # print("raw_data:原始数据:", raw_data)

        data_list_json[head_id] = raw_data
        data_list_device_id_json[head_id] = device_id

        # 构造："081010":[X，Y]
        # key = datetime.datetime.strptime(start_time, "%Y%m%d:%H")
        # key = "%s-%s" % (start_time.strftime("%Y%m%d:%H"), device_id)
        key = "%s-%s_%s" % (start_time.strftime("%Y%m%d:%H"), device_id, str(head_id))    # 综合对比图图例名称
        (colX, colY) = getDataByColumnsV2(raw_data)

        # 原位数据和激光雷达数据分开存放和画图
        if "System6" in file_store_path:
            if value_type not in data_yw_balloon_value_type_obj.keys():
                data_yw_balloon_value_type_obj[value_type] = {}
            data_yw_balloon_value_type_obj[value_type][key] = (colX, colY)
        elif "System7" in file_store_path:
            if value_type not in data_yw_rocket_value_type_obj.keys():
                data_yw_rocket_value_type_obj[value_type] = {}
            data_yw_rocket_value_type_obj[value_type][key] = (colX, colY)
        else:
            if value_type not in data_linda_value_type_obj.keys():
                data_linda_value_type_obj[value_type] = {}
            data_linda_value_type_obj[value_type][key] = (colX, colY)

    print("按data_value归类后数据（激光雷达）：", data_linda_value_type_obj)
    print("按data_value归类后数据（原位气球）：", data_yw_balloon_value_type_obj)
    print("按data_value归类后数据（原位火箭）：", data_yw_rocket_value_type_obj)
    # return -1

    if len(data_list_json.keys()) == 0:
        print('未找到原始探测数据！')
        return -1

    # # 测试用开始
    # conf['data_list_json'] = data_list_json
    # conf['data_list_device_id_json'] = data_list_device_id_json
    # conf['figure_name_list_ld'] = figure_name_list_ld
    # conf['test_flag'] = False
    # #测试用结束

    # 1. 针对同类（激光雷达/原位气球/原位火箭）的所有廓线生成综合廓线图像 TODO 是否有必要改成多线程方式
    # 仅需要原始探测数据
    # 生成激光雷达多廓线图
    conf['SysType'] = 0
    drawingFiguringMulti(conf, data_linda_value_type_obj, figure_name_list_ld_map)
    # 生成原位气球探测多廓线图
    conf['SysType'] = 1
    drawingFiguringMulti(conf, data_yw_balloon_value_type_obj, figure_name_list_yw_balloon_map)
    # 生成原位火箭探测多廓线图
    conf['SysType'] = 2
    drawingFiguringMulti(conf, data_yw_rocket_value_type_obj, figure_name_list_yw_rocket_map)

    # time.sleep(2)
    return {"code": 0, "msg": "多廓线综合比较图生成完成"}


# 生成廓线图（支持一张图中多条廓线）
def drawingFiguringMulti(conf, data_linda_value_type_obj, figure_labels_map):
    if len(data_linda_value_type_obj.keys()) == 0:
        if conf['SysType'] == 1:
            print("原位探测气球数据为空，无法绘制廓线图..")
        elif conf['SysType'] == 2:
            print("原位探测火箭数据为空，无法绘制廓线图..")
        else:
            print("激光雷达探测数据为空，无法绘制廓线图..")
        return

    # # TODO 删除（替代图片）
    # # 测试代码开始
    # if conf['test_flag']:
    #     skip_list = ["激光-密度-比对图", "激光-温度-比对图", "激光-风场-比对图", "激光-铁密度-比对图", "激光-钠密度-比对图"]
    #     for head_id in conf['data_list_json'].keys():
    #         raw_data = conf['data_list_json'][head_id]
    #         device_id = conf['data_list_device_id_json'][head_id]
    #         # print(raw_data)
    #         for figure_name_tag in conf['figure_name_list_ld']:
    #             if figure_name_tag not in skip_list:
    #                 runFunctionFiguring(raw_data, conf, figure_name_tag, device_id)
    #         break
    # # 测试代码结束

    # 生成综合对比图
    ret_obj = {}
    # 最大廓线数量默认为30
    MAX_FIGURE = conf['MaxDataNumber'] if 'MaxDataNumber' in conf.keys() else 30
    for value_type,data_obj in data_linda_value_type_obj.items():
        # 参量类型，及探测值默认单位
        data_type, xlabel = checkL2DataTypeAndUnitByValueType2309(value_type)
        data_type = "W" if (data_type == "W1" or data_type == "W2") else data_type
        print("value_type|data_type|xlabel:", value_type, data_type, xlabel)

        figure_name_tag = figure_labels_map[data_type] if data_type in figure_labels_map.keys() else ""
        print("X_Label: %s, %s" % (xlabel, figure_name_tag))
        # 没有图标题，不画图
        if figure_name_tag == "":
            continue

        # 合法类型，开始画图
        # if '[device_id]' in figure_name_tag:
        #     figure_name_tag = figure_name_tag.replace("[device_id]", str(device_id))
        # 图像保存路径。例如：media/data/report_figure/1705150950779/激光雷达-密度-廓线.png
        fig_save_path = "%s/%s" % (conf['FigSavePath'], conf['TaskId'])
        if not os.path.exists(fig_save_path):
            try:
                os.makedirs(fig_save_path)
            except Exception as e:
                print("创建报告图片路径错误：", e)
        fig_save_full_path = "%s/%s.png" % (fig_save_path, figure_name_tag)
        print("参数：", fig_save_path, figure_name_tag)

        # 清空画布，否则后面画的图像包含前面的图像
        plt.clf()
        num_plots = len(data_obj) if len(data_obj) <= MAX_FIGURE else MAX_FIGURE     # 廓线总数，影响到图中线的颜色差异性
        # plt.gca().set_color_cycle([colormap(i) for i in np.linspapce(0, 0.9, num_plots)])
        plt.gca().set_prop_cycle(plt.cycler('color', plt.cm.jet(np.linspace(0, 1, num_plots))))
        labels = []
        count = 0
        for label,data in data_obj.items():
            # print("H：", data[0])     #高度
            # print("V：", data[1])     #探测值
            # 大气密度用对数坐标系
            if value_type == 4:
                plt.semilogx(data[1], data[0])
            else:
                plt.plot(data[1], data[0])
            labels.append(label)

            count += 1
            if count >= MAX_FIGURE:
                print("[综合对比图]已达到最大廓线数量:", MAX_FIGURE)
                break

        # 画图
        plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
        plt.xlabel(xlabel, fontsize=13, color='black')     #TODO
        plt.ylabel('高度/km', fontsize=13, color='black')
        plt.legend(labels, ncol = 4, loc='upper center', bbox_to_anchor=[0.5,1.15], columnspacing=1.5, labelspacing=0.0, handletextpad=0.0, handlelength=1.5, fancybox=True, shadow=True)
        # plt.savefig(fig_save_full_path, dpi=72)     # 保存分辨率为72的图片
        # 保存图片背景透明，bbox_inches和pad_inches的设置是为了保存图像时删除图像的白边
        plt.savefig(fig_save_full_path, dpi=128, transparent=True, bbox_inches='tight', pad_inches=0.0)
        # plt.show()
        saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_tag, fig_save_full_path)

        plt.close()

        # 记录参量类型画图包含廓线数量
        ret_obj[value_type] = len(data_obj)

    print("【完成】生成多条廓线综合视图..", ret_obj)
    return 0


# 2. 生成单条廓线的：折线图、棒状图、饼图
# 图 LD1温度测量不确定度分量（饼图）
# 图 LD1温度标准不确定度（折线图）
# 图 LD1温度廓线及其标准不确定度（棒状图）
def taskReportFigureGenWithGumResult(data_head_id_list, conf, data_level, all_devices_obj, TASK_ID):
    print("饼图、折线图、棒状图生成：")
    conf['DbTable'] = 'bt_data'
    print(data_head_id_list)
    print(conf)

    # 获取最后一次评定的batch_id
    batch_id = ""
    # print("取最新一次任务处理结束的数据..")
    # data_batch_info = getLatestDataBatchId(2)
    # if data_batch_info == "":
    #     print("错误：未找到最近录入的数据")
    # else:
    #     batch_id = data_batch_info['KeyName']

    # 罗列数据库所有U值字段
    u_list_totle = []
    for i in range(1, 23):
        u_list_totle.append("U" + str(i))
    print(u_list_totle)

    # 图像保存路径检查。例如：media/data/report_figure/1705150950779/激光雷达-密度-廓线.png
    fig_save_path = "%s/%s" % (conf['FigSavePath'], conf['TaskId'])
    if not os.path.exists(fig_save_path):
        try:
            os.makedirs(fig_save_path)
        except Exception as e:
            print("创建报告图片路径错误：", e)

    # 根据head_id，测量数据类型value_type，获取评定后的数据
    count = 0
    # MAX_FIGURE = 30    # 控制生成的最大廓线数量
    MAX_FIGURE = conf['MaxDataNumber'] if 'MaxDataNumber' in conf.keys() else 30
    # MAX_FIGURE = 5
    for (head_id, value_type, start_time, end_time, station_id, device_id, file_name, file_store_path) in data_head_id_list:
        print("==================================")
        print("head_id = %d, value_type = %s" % (head_id, value_type))
        #################################################
        # 根据device_id获取设备名称device_name，用于画图名称
        if all_devices_obj != "" and device_id in all_devices_obj.keys():
            device_name = all_devices_obj[device_id]['DeviceName2']
            if device_name.strip() == "":
                device_name = str(device_id)
        #####
        # 数据获取步骤
        # 获取评定后的L2数据（包括GUM值及分量值）
        print("batch_id: ", batch_id)
        if batch_id != "":
            raw_data = getRawDataByHeadIdAndBatchIdInJsonObjectList(conf, head_id, batch_id)
        else:
            raw_data = getRawDataByHeadIdInJsonObjectList(conf, head_id)
        # 按高度排序
        raw_data = sorted(raw_data, key=lambda x: x['Height'])
        if raw_data == "" or len(raw_data) == 0:
            print("错误：未找到最近录入的原始数据")
            continue

        print("raw_data:原始数据条数: %d" % len(raw_data))
        # print("raw_data:原始数据:", raw_data)
        # return

        ###################################################
        # 判断数据类型步骤
        # 判断激光雷达，还是原位探测
        # 原位数据和激光雷达数据分开存放和画图
        if "System6" in file_store_path:
            # 原位气球
            uncertainty_component_name = uncertainty_component_name_yw
            figure_labels_map = figure_name_list_yw_balloon_map
        elif "System7" in file_store_path:
            # 原位火箭
            uncertainty_component_name = uncertainty_component_name_yw
            figure_labels_map = figure_name_list_yw_rocket_map
        else:
            # 激光雷达
            uncertainty_component_name = uncertainty_component_name_ld
            figure_labels_map = figure_name_list_ld_map

        # 参量类型，及探测值默认单位
        data_type, xlabel = checkL2DataTypeAndUnitByValueType2309(value_type, 1)   # 获取不确定度评定结果单位（折线图单位）（密度无棒状图，故不影响）
        data_type = "W" if (data_type == "W1" or data_type == "W2") else data_type
        print("value_type|data_type|xlabel:", value_type, data_type, xlabel)

        # 饼图名称
        fig_label_key = "%s-Pie" % data_type
        figure_name_pie = figure_labels_map[fig_label_key] if fig_label_key in figure_labels_map.keys() else ""
        # 折线图名称
        fig_label_key = "%s-Con" % data_type
        figure_name_con = figure_labels_map[fig_label_key] if fig_label_key in figure_labels_map.keys() else ""
        # 棒状图名称
        fig_label_key = "%s-Err" % data_type
        figure_name_err = figure_labels_map[fig_label_key] if fig_label_key in figure_labels_map.keys() else ""

        # sub_line = "No." + str(head_id)
        # 2023-08-20 13:00:00 ==> 2023-08-20_13
        # 正式系统
        # sub_line = get_time_hour(start_time).split(':')[0].replace(' ', '_')
        # 为区分同一时间的不同廓线，添加head_id
        # sub_line = get_time_hour(start_time).split(':')[0].replace(' ', '_') + "No." + str(head_id)
        sub_line = "%sNo.%s" % (start_time.strftime("%Y%m%d_%H"), str(head_id))

        ###############################################
        # 画图步骤：
        # 1. 画饼图（三张）
        # figure_name_pie = ""    # 测试+调试
        if figure_name_pie != "":
            print("画饼图..")
            # 处理文件名
            if '[device_id]' in figure_name_pie:
                figure_name_pie_new = figure_name_pie.replace("[device_id]", str(device_id))    # 用于图保存名称
                figure_title_pie = figure_name_pie.replace("[device_id]", device_name)          # 用于图中title显示
            figure_name_tag = figure_name_pie_new + sub_line
            figure_title_tag = figure_title_pie + sub_line

            # 构造："081010":[X，Y，GUM, GUM_EXTEND, (U1,U2,...)]
            # 获取图例：获取不为空的分量U和其名称（根据第一个高度判断）
            u_list, u_name_list = getNotNullU(raw_data[0], u_list_totle, uncertainty_component_name)
            print("分量名称：", u_list, u_name_list)
            # 获取数据：分别获取高度的最小值、最大值、中间值对应不确定度分量画饼图
            h_min_u_values, h_max_u_values, h_middle_u_values, height_list = getNotNullUValues(raw_data, u_list)
            print("饼图高度取样：", height_list)

            # 画图
            num = 0
            for fig_data in [h_min_u_values, h_max_u_values, h_middle_u_values]:
                fig_save_full_path = "%s/%s-%d.png" % (fig_save_path, figure_name_tag, num + 1)
                title = "%s(高度=%skm)" % (figure_title_tag, height_list[num])
                print("饼图名称：", fig_save_full_path, title)
                drawPieFigure(u_name_list, fig_data, fig_save_full_path, title)
                saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_pie_new, fig_save_full_path)
                num += 1

        # 2. 画折线图
        # figure_name_con = ""   # 测试+调试
        if figure_name_con != "":
            print("画折线图..")
            # 处理文件名
            if '[device_id]' in figure_name_con:
                figure_name_con_new = figure_name_con.replace("[device_id]", str(device_id))    # 用于图保存名称
                figure_title_con = figure_name_con.replace("[device_id]", device_name)          # 用于图中title显示
            figure_name_tag = figure_name_con_new + sub_line
            figure_title_tag = figure_title_con + sub_line

            (colX, colY, colGum, colGumExtend) = getGumResultVectorByColumnsList(raw_data)
            fig_save_full_path = "%s/%s.png" % (fig_save_path, figure_name_tag)
            drawContourFigure(colX, colGum, xlabel, fig_save_full_path, figure_title_tag)
            saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_con_new, fig_save_full_path)

        # 3. 画棒状图
        # figure_name_err = ""   # 测试+调试
        if figure_name_err != "":
            print("画棒状图..")
            # 处理文件名
            if '[device_id]' in figure_name_err:
                figure_name_err_new = figure_name_err.replace("[device_id]", str(device_id))    # 用于图保存名称
                figure_title_err = figure_name_err.replace("[device_id]", device_name)          # 用于图中title显示
            figure_name_tag = figure_name_err_new + sub_line
            figure_title_tag = figure_title_err + sub_line

            (colX, colY, colGum, colGumExtend) = getGumResultVectorByColumnsList(raw_data)
            fig_save_full_path = "%s/%s.png" % (fig_save_path, figure_name_tag)
            drawErrorBarChartFigure(colX, colY, colGum, xlabel, fig_save_full_path, figure_title_tag)
            print("post_1")
            saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_err_new, fig_save_full_path)


        # return 0 # 测试
        count += 1
        # 控制廓线数量
        if count >= MAX_FIGURE:
            print("[单条廓线画图]已达到最大廓线数量:", MAX_FIGURE)
            return count

    return count


# 画折线图
def drawContourFigure(Y, X, xlabel, fig_save_full_path, title):
    print("画折线图..")
    print(Y, X)
    # 输入数据
    # X = np.linspace(0, 10, 100)  # x轴数据
    # Y = np.sin(x)  # y轴数据

    # 清空画布，否则后面画的图像包含前面的图像
    plt.clf()
    # 设置画图属性
    plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号

    # 创建图形对象
    fig, ax = plt.subplots()

    # 绘制折线图
    ax.plot(X, Y, label='GUM')

    # 设置标题和标签
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel('高度/km')
    # plt.xlabel(xlabel, fontsize=13, color='black')     #TODO
    # plt.ylabel('高度/km', fontsize=13, color='black')

    # 添加图例
    ax.legend()

    # 保存图片
    plt.savefig(fig_save_full_path, dpi=128, transparent=True, bbox_inches='tight', pad_inches=0.0)
    # 显示图形
    # plt.show()

    plt.close()

    return 0


# 画饼图
# labels:图例；size:数据
def drawPieFigure(labels, sizes, fig_save_full_path, title):
    print("饼图drawPieFigure..")
    print(labels, sizes)
    labels_for_pie = [None for _ in range(len(labels))]   # 饼图不显示文字标签，只显示各部分数值（文字内容可参考图例）
    # 输入数据
    # labels = ['A', 'B', 'C', 'D']  # 部分名称
    # sizes = [30, 25, 15, 30]  # 部分所占比例

    # 清空画布，否则后面画的图像包含前面的图像
    plt.clf()
    # 指定显示的pie是正圆
    # plt.axis('equal')

    # 设置画图属性
    plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号

    sizes = [float(x) for x in sizes]
    # 计算每部分所占的百分比
    if sum(sizes) != 0.0:
    #if sum(sizes) - 0 >= EPSINON:   # 不等于0
        percentages = [(x / sum(sizes)) * 100 for x in sizes]
    else:
        percentages = [0.001 for x in sizes]

    # 创建图形对象
    fig, ax = plt.subplots()

    # 设置饼图的大小和位置
    # plt.figure(figsize=(6, 6), dpi=80)

    # 绘制饼图
    # labeldistance=1.1 调整标签位置防止重叠
    # wedges, texts, autotexts = ax.pie(percentages, labels=labels, labeldistance=1.1, autopct='%1.1f%%')
    # 不显示百分比数据
    wedges, texts = ax.pie(percentages, labels=labels_for_pie, labeldistance=1.1)

    # 显示原始数据
    for i, (wedge, label) in enumerate(zip(wedges, labels)):
        angle = 0.5 * (wedge.theta1 + wedge.theta2)
        x = np.cos(np.deg2rad(angle))
        y = np.sin(np.deg2rad(angle))
        ax.text(x, y, f'{sizes[i]}', ha='center', va='center')

    # 设置饼图中的文字属性
    for text in texts:
        text.set_fontsize(12)
        text.set_fontweight('bold')

    # 设置饼图中的百分比数值属性
    # for autotext in autotexts:
    #     autotext.set_fontsize(12)
    #     autotext.set_fontweight('bold')

    # 添加图例
    # ax.legend(wedges, labels, loc='best')
    ax.legend(wedges, labels, loc='upper right')
    # ax.legend(loc='upper right', bbox_to_anchor=(1, 1), borderaxespad=-0.1)

    # 设置标题
    ax.set_title(title)

    # 调整图形布局
    plt.tight_layout()

    # 保存图片
    plt.savefig(fig_save_full_path, dpi=128, transparent=True, bbox_inches='tight', pad_inches=0.0)
    # 显示图形
    # plt.show()

    plt.close()

    return 0


# 画棒状图
def drawErrorBarChartFigure(Y, X, error, xlabel, fig_save_full_path, title):
    print("画棒状图drawErrorBarChartFigure..")
    print(len(X), len(Y), len(error))
    print(Y, X, error)
    # 检查 error 向量，替换掉None值为0
    error = [0 if (x is None or math.isnan(x) or x == "nan" or x == "None") else x for x in error]
    # print(Y, X, error)

    # 输入数据
    # X = np.linspace(0, 10, 10)  # x轴数据
    # Y = np.sin(x)  # y轴数据
    # error = np.random.rand(10) * 0.5  # 误差数据

    # 清空画布，否则后面画的图像包含前面的图像
    plt.clf()
    # 设置画图属性
    plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号

    # 创建图形对象
    fig, ax = plt.subplots()

    # 绘制折线图
    # ax.plot(X, Y, linestyle='-', label='')
    ax.plot(X, Y, linestyle='-')

    # 绘制横向误差棒
    ax.errorbar(
        X, Y,
        xerr=error,
        fmt='none',
        ecolor='red',  # 误差棒颜色
        elinewidth=2,  # 误差棒线条粗细
        capsize=1,     # 误差棒末端大小
        errorevery=1,  # 误差棒显示间隔
        barsabove=True  # 误差棒显示在数据点上方
    )

    # 设置标题和标签
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel('高度/km')

    # 添加图例
    ax.legend()

    # 保存图片
    plt.savefig(fig_save_full_path, dpi=128, transparent=True, bbox_inches='tight', pad_inches=0.0)
    # 显示图形
    # plt.show()

    plt.close()

    return 0


# 报告图片存库
def saveReportFigureResultIntoDB(report_id, algorithm_type, figure_name_tag, location):
    print("报告生成图结果保存：")
    cols = ["ReportId", "AlgorithmType", "FigureName", "FigureLocation"]
    col_str = ','.join(cols)
    value_str = '"%s", "%d", "%s", "%s"' % (report_id, algorithm_type, figure_name_tag, location)

    print("报告图片信息： %s" % value_str)
    head_id = insert_db(db_table_report_figure, col_str, value_str)
    print("报告图片信息插入ID： %d" % head_id)
    if head_id == -1:
        return "[Error]：报告图片信息插入数据库错误！"

    return head_id







########################
# 测试函数
# 生成廓线图
def runFunctionFiguringMulti(raw_data_obj, conf, figure_name_tag, device_id):
    print("raw_data:XXXXXXXX")
    # print(raw_data)
    try:
        (colX, colY) = getDataByColumnsV2(raw_data)
        print(colX)
        print(colY)
    except Exception as e:
        print("Data_get_columns_Error: %s", e)
        return {"code": -1, "msg": "数据获取列格式错误！"}

    # 决定X轴Label
    # TODO 探测值和单位需用head_id从表bt_temporary_data_head中查询（已导入该数据）
    xlabel = "探测值"
    if raw_data[0]['ValueType'] == 0:
        xlabel = '温度/K'
    elif raw_data[0]['ValueType'] == 1:
        xlabel = '密度/(kg/m3)'
    elif raw_data[0]['ValueType'] == 2:
        xlabel = '纬向风速/(m/s)'
    elif raw_data[0]['ValueType'] == 3:
        xlabel = '径向风速/(m/s)'
    print("X_Label: %s", xlabel)

    #
    if '[device_id]' in figure_name_tag:
        figure_name_tag = figure_name_tag.replace("[device_id]", str(device_id))
    # 图像保存路径。例如：media/data/report_figure/1705150950779/激光雷达-密度-廓线.png
    fig_save_path = "%s/%s" % (conf['FigSavePath'], conf['TaskId'])
    if not os.path.exists(fig_save_path):
        try:
            os.makedirs(fig_save_path)
        except Exception as e:
            print("创建报告图片路径错误：", e)
    fig_save_full_path = "%s/%s.png" % (fig_save_path, figure_name_tag)

    # 最小二乘法多项式拟合
    coef = functionFiguring(colX, colY, 3, fig_save_full_path, xlabel)
    saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_tag, fig_save_full_path)

    return 0


def functionFiguring(x, y, n, fig_save_path, xlabel="探测值"):
    """
    传入数据x,y,
    多项式次数m
    """
    # 清空画布，否则后面画的图像包含前面的图像
    plt.clf()

    x = np.array(x)
    y = np.array(y)
    fit_coef = np.polyfit(x, y, n)      # 计算拟合系数
    # poly1d将这些系数与未知数结合起来，在python中构造出一个数学的函数。（显示用，非必须）
    print(np.poly1d(np.polyfit(x,y,deg=n)))

    # 绘制图像
    fit_coef = [round(i,3) for i in fit_coef]
    # plt.plot(x, y, '^')
    plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
    #plt.plot(y, x, '^')
    # plt.plot(y, x, label='拟合数据')              # 普通画图
    plt.plot(y, x, label='拟合数据', color='#1ff78c', linewidth=2.5)     # 设置画图线条颜色和粗细
    # 改变坐标轴刻度颜色
    plt.tick_params(axis='x', colors='white')       # 坐标刻度颜色
    plt.tick_params(axis='y', colors='white')       # 坐标刻度颜色
    plt.xticks(fontsize=13)                         # 坐标刻度字体大小
    plt.yticks(fontsize=13)                         # 坐标刻度字体大小
    #plt.axis('off')                                # 去掉坐标轴
    # plt.grid(axis="y", linestyle = '--', linewidth = 0.5)    # 背景加横向的网格
    plt.grid(axis="y", linewidth = 0.3)    # 背景加横向的网格
    # 隐藏边线，保留刻度线和标签
    ax = plt.axes()
    for i in ['top', 'right', 'bottom', 'left']:
        ax.spines[i].set_visible(False)

    # 坐标轴图标颜色
    plt.xlabel(xlabel, fontsize=13, color='white')     #TODO
    plt.ylabel('高度/km', fontsize=13, color='white')
    plt.legend()

    # plt.savefig(fig_save_path, dpi=72)     # 保存分辨率为72的图片
    # 保存图片背景透明，bbox_inches和pad_inches的设置是为了保存图像时删除图像的白边
    plt.savefig(fig_save_path, dpi=72, transparent=True, bbox_inches='tight', pad_inches=0.0)
    #plt.show()
    return fit_coef


# 生成廓线图
def runFunctionFiguring(raw_data, conf, figure_name_tag, device_id):
    print("raw_data:XXXXXXXX")
    # print(raw_data)
    try:
        (colX, colY) = getDataByColumnsV2(raw_data)
        print(colX)
        print(colY)
    except Exception as e:
        print("Data_get_columns_Error: %s", e)
        return {"code": -1, "msg": "数据获取列格式错误！"}

    # 决定X轴Label
    # TODO 探测值和单位需用head_id从表bt_temporary_data_head中查询（已导入该数据）
    xlabel = "探测值"
    if raw_data[0]['ValueType'] == 0:
        xlabel = '温度/K'
    elif raw_data[0]['ValueType'] == 1:
        xlabel = '气压/(Pa)'
    elif raw_data[0]['ValueType'] == 2:
        xlabel = '纬向风速/(m*s-1)'
    elif raw_data[0]['ValueType'] == 3:
        xlabel = '径向风速/(m*s-1)'
    elif raw_data[0]['ValueType'] == 4:
        xlabel = '密度/(kg/m3)'
    elif raw_data[0]['ValueType'] == 5:
        xlabel = '钠原子密度/(cm-3)'
    elif raw_data[0]['ValueType'] == 6:
        xlabel = '铁原子密度/(cm-3)'
    print("X_Label: %s", xlabel)

    #
    if '[device_id]' in figure_name_tag:
        figure_name_tag = figure_name_tag.replace("[device_id]", str(device_id))
    # 图像保存路径。例如：media/data/report_figure/1705150950779/激光雷达-密度-廓线.png
    fig_save_path = "%s/%s" % (conf['FigSavePath'], conf['TaskId'])
    if not os.path.exists(fig_save_path):
        try:
            os.makedirs(fig_save_path)
        except Exception as e:
            print("创建报告图片路径错误：", e)
    fig_save_full_path = "%s/%s.png" % (fig_save_path, figure_name_tag)

    # 最小二乘法多项式拟合
    coef = functionFitting(colX, colY, 3, fig_save_full_path, xlabel)
    saveReportFigureResultIntoDB(conf['TaskId'], 0, figure_name_tag, fig_save_full_path)

    return 0


##########################################
# 数据拟合功能
# 最小二乘法拟合多项式
# 参考：https://baijiahao.baidu.com/s?id=1643468349140251072&wfr=spider&for=pc
def functionFitting(x, y, n, fig_save_path, xlabel="探测值"):
    """
    传入数据x,y,
    多项式次数m
    """
    # 清空画布，否则后面画的图像包含前面的图像
    plt.clf()

    x = np.array(x)
    y = np.array(y)
    fit_coef = np.polyfit(x, y, n)      # 计算拟合系数
    # poly1d将这些系数与未知数结合起来，在python中构造出一个数学的函数。（显示用，非必须）
    print(np.poly1d(np.polyfit(x,y,deg=n)))

    # 绘制图像
    fit_coef = [round(i,3) for i in fit_coef]
    # plt.plot(x, y, '^')
    plt.rcParams['font.sans-serif']=['SimHei']      # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
    #plt.plot(y, x, '^')
    # plt.plot(y, x, label='拟合数据')              # 普通画图
    plt.plot(y, x, label='拟合数据', color='#1ff78c', linewidth=2.5)     # 设置画图线条颜色和粗细
    # 改变坐标轴刻度颜色
    plt.tick_params(axis='x', colors='white')       # 坐标刻度颜色
    plt.tick_params(axis='y', colors='white')       # 坐标刻度颜色
    plt.xticks(fontsize=13)                         # 坐标刻度字体大小
    plt.yticks(fontsize=13)                         # 坐标刻度字体大小
    #plt.axis('off')                                # 去掉坐标轴
    # plt.grid(axis="y", linestyle = '--', linewidth = 0.5)    # 背景加横向的网格
    plt.grid(axis="y", linewidth = 0.3)    # 背景加横向的网格
    # 隐藏边线，保留刻度线和标签
    ax = plt.axes()
    for i in ['top', 'right', 'bottom', 'left']:
        ax.spines[i].set_visible(False)

    # 坐标轴图标颜色
    plt.xlabel(xlabel, fontsize=13, color='white')     #TODO
    plt.ylabel('高度/km', fontsize=13, color='white')
    plt.legend()

    # plt.savefig(fig_save_path, dpi=72)     # 保存分辨率为72的图片
    # 保存图片背景透明，bbox_inches和pad_inches的设置是为了保存图像时删除图像的白边
    plt.savefig(fig_save_path, dpi=72, transparent=True, bbox_inches='tight', pad_inches=0.0)
    #plt.show()
    return fit_coef
